﻿using System;
using System.Collections.Generic;

namespace VietNamKey
{
	public class Vietnamese
	{
		private List<string> NguyenAm  = new List<string>(new[]{"a", "ă", "â", "e", "ê", "i", "y", "o", "ô", "ơ", "u", "ư", "ai", "ao", "au", "ay", "âu", "ây", "eo", "êu", "ia", "iê", "iu", "oa", "oă", "oe", "oi", "ôi", "ơi", "ua", "uâ", "uê", "ui", "uô", "uơ", "uy", "ưa", "ưi", "ươ", "ưu", "oo", "ôô", "iêu", "yêu", "oai", "oay", "uây", "uôi", "uyê", "uyu", "ươi", "ươu", "uya", "oao", "oeo"});
		private List<string> NguyenAmGiua = new List<string>(new[]{"ă", "â", "iê", "oă", "uâ", "uô", "oo", "ôô", "ươ", "uyê"});
		private List<string> NguyenAmCuoi = new List<string>(new[]{"ai", "ao", "au", "ay", "âu", "ây", "eo", "êu", "ia", "iu", "oi", "ôi", "ơi", "ui", "ưa", "ưi", "ưu", "iêu", "uôi", "uyu", "ươi", "ươu", "oai", "oay", "uây", "uya", "oeo", "oao"});
		private List<string> PhuAm  = new List<string>(new[]{"b", "c", "k", "qu", "d", "đ", "g", "h", "l", "m", "n", "p", "r", "s", "t", "v", "x", "ch", "gi", "kh", "nh", "ng", "ngh", "ph", "th", "tr"});
        private List<string> PhuAmCuoi = new List<string>(new[]{"c", "ch", "m", "n", "ng", "nh", "p", "t"});
        private List<string> SacNang = new List<string>(new[]{"c", "ch", "p", "t"});
        private List<string> UNI = Encodings.Unicode.Value;
		
		private List<string> a, o, e, u, uw, aoe, uo, uoa;
		public Vietnamese()
		{
			SetSetting(TypingMethods.Telex.Value, UNI, true, true, 2);
		}
		
		public void SetSetting(List<char> typingMethod,  List<string> encoding, bool checkSpelling, bool useNewStype, int checkSpellingLevel)
		{
			TypingMethod = typingMethod;
			Encoding = encoding;
			CheckSpelling = checkSpelling;
			UseNewStyle = useNewStype;
			CheckSpellingLevel = checkSpellingLevel;
			a = GetRange("aăâ");
			o = GetRange("oôơ");
			e = GetRange("eê");
			u = GetRange("uư");
			uw = GetRange("ư");
			aoe = GetRange("aăâoôơeê");
			uo = GetRange("uưoôơ");
			uoa = GetRange("uưoôơaăâ");
		}
		
		public Vietnamese(ItemTypingMethod typingMethod, ItemEncoding encoding, bool checkSpelling, bool useNewStype, int checkSpellingLevel)
		{
			SetSetting(typingMethod.Value, encoding.Value, checkSpelling, useNewStype, checkSpellingLevel);
		}
		public bool CheckSpelling { get; set; }
		public int CheckSpellingLevel { get; set; }
		public bool UseNewStyle { get; set; }
		public List<char> TypingMethod { get; set; }
		public List<string> Encoding { get; set; }
		/// <summary>
		/// Lấy danh sách các chữ có thể bỏ dấu mũ, móc được
		/// </summary>
		/// <param name="BeginChars">chữ thường a, o, u, e VD: "a" hoặc "aeo"</param>
		/// <returns>danh sách chữ dựa vào bảng mã đang chọn</returns>
		public List<string> GetRange(string BeginChars)
		{
			List<string> result = new List<string>();
			for(int i = 0; i < BeginChars.Length; i++)
			{
				int bg = UNI.IndexOf(BeginChars[i].ToString());
				for(int j = 0; j < 6; j++)
					result.AddRange(new string[]{UNI[bg + j], UNI[bg + 74 + j]});
			}
			return result;
		}
		
		public string ToVN(string sText)
		{
			int i = sText.Length - 1;
			string stxt = sText.Substring(0, i);
            char cCurChar = sText.Substring(i, 1).ToLower()[0];
            if(CheckSpelling)
               if(!CheckLastConsonant(stxt, cCurChar) && !(cCurChar == TypingMethod[TMD.DD] && stxt.Length == 1))
	            {// kiểm tra phụ âm cuối
	            	if(cCurChar == TypingMethod[TMD.UOAW])
	    				return stxt + (char.IsUpper(sText.Substring(i, 1)[0]) ? "Ư" : "ư");
	            	return sText;
            	} else {}
            else if(cCurChar == TypingMethod[TMD.UOAW])
				return stxt + (char.IsUpper(sText.Substring(i, 1)[0]) ? "Ư" : "ư");
			string sPreTxt = i - 1 > 0 ? sText.Substring(0, i - 1) : "";
			string sNextTxt = "";
            string sPreChar = sText.Substring(i - 1, 1);
            bool Ok = Check(sPreTxt, sPreChar, cCurChar, i, i - 1);
            if(!Ok)
	            for (int j = sPreTxt.Length - 1; j >= 0; j--)
	            	if(sPreTxt.Length > 0)
                   	{
                   		sNextTxt = sPreChar + sNextTxt;
                   		sPreChar = sPreTxt.Substring(sPreTxt.Length - 1, 1);
                   		sPreTxt = sPreTxt.Substring(0, sPreTxt.Length - 1);
                   		if(Check(sPreTxt, sPreChar,cCurChar, i, j)) 
                   		{
                   			Ok = true;
                   			break;
                   		}
                   	}
            if(!Ok && !(cCurChar == TypingMethod[TMD.DD] && stxt.Length == 1)) return sText;
            
            int index = UNI.IndexOf(sPreChar);
           	if(cCurChar == TypingMethod[TMD.Sac]
        		|| cCurChar == TypingMethod[TMD.Huyen]
        		|| cCurChar == TypingMethod[TMD.Nga]
        		|| cCurChar == TypingMethod[TMD.Hoi]
        		|| cCurChar == TypingMethod[TMD.Nang]
    			|| cCurChar == TypingMethod[TMD.XoaDau])
            {
            	string newChar = UNI[index - GetSignIndex(index) + TypingMethod.IndexOf(cCurChar)];
            	string removeChar = UNI[index - GetSignIndex(index)];
            	return sPreTxt + (newChar == sPreChar ? removeChar + sNextTxt + cCurChar: newChar + sNextTxt);
            }
            
            if(cCurChar == TypingMethod[TMD.AA] && a.Contains(sPreChar))
            {
            	string newChar = UNI[index + 12 - SubtracIndex(sPreChar)];
            	string removeChar = UNI[index - SubtracIndex(sPreChar)];
            	return sPreTxt + (newChar == sPreChar ? removeChar + sNextTxt + cCurChar: newChar + sNextTxt);
            }
            
            if(cCurChar == TypingMethod[TMD.DauMuAOE] && aoe.Contains(sPreChar))
            {
            	string newChar = UNI[index + (a.Contains(sPreChar) ? 12 : 6) - SubtracIndex(sPreChar)];
            	string removeChar = UNI[index - SubtracIndex(sPreChar)];
            	sText =  sPreTxt + (newChar == sPreChar ? removeChar + sNextTxt + cCurChar: newChar + sNextTxt); 
            	if(newChar == sPreChar && o.Contains(sPreChar))
				{	
					string ppc = sPreTxt.Length > 0 ? sPreTxt.Substring(sPreTxt.Length - 1) : "-";
					if(u.Contains(ppc))
						sText = (sPreTxt.Length > 1 ? sPreTxt.Substring(0, sPreTxt.Length - 1) : "") + UNI[UNI.IndexOf(ppc) - SubtracIndex(ppc)] + removeChar + sNextTxt + cCurChar;
				}
            	return sText;
            }
                        
            if((cCurChar == TypingMethod[TMD.AW] && a.Contains(sPreChar))
	            	||(cCurChar == TypingMethod[TMD.EE] && e.Contains(sPreChar))
	            	||(cCurChar == TypingMethod[TMD.OThanh6] && o.Contains(sPreChar)))
            {
            	string newChar = UNI[index + 6 - SubtracIndex(sPreChar)];
            	string removeChar = UNI[index - SubtracIndex(sPreChar)];
            	sText = sPreTxt + (newChar == sPreChar ? removeChar + sNextTxt + cCurChar: newChar + sNextTxt);				
            	if(o.Contains(sPreChar))
				{	
					string ppc = sPreTxt.Length > 0 ? sPreTxt.Substring(sPreTxt.Length - 1) : "-";
					int pindex = UNI.IndexOf(ppc);
					if(u.Contains(ppc))
						sText = (sPreTxt.Length > 1 ? sPreTxt.Substring(0, sPreTxt.Length - 1) : "") + UNI[pindex - SubtracIndex(ppc) - (newChar == sPreChar ? GetSignIndex(pindex) : 0)] + (newChar == sPreChar ? removeChar + sNextTxt + cCurChar: newChar + sNextTxt);
				}
            	return sText;
            }
			
            if((cCurChar == TypingMethod[TMD.DauMocUO] && uo.Contains(sPreChar))
               ||((cCurChar == TypingMethod[TMD.UOAW] || cCurChar == TypingMethod[TMD.DauMocUOA]) && uoa.Contains(sPreChar)))
            {
            	string newChar = UNI[index + (o.Contains(sPreChar) ? 12 : 6) - SubtracIndex(sPreChar)];
            	string removeChar = UNI[index - SubtracIndex(sPreChar)];
            	sText = sPreTxt + (newChar == sPreChar ? removeChar + sNextTxt + cCurChar: newChar + sNextTxt);
            	if(o.Contains(sPreChar)) //uow => ươ !!! huơ & quơ
				{	
					string p1 = sPreTxt.Length > 0 ? sPreTxt.Substring(sPreTxt.Length - 1) : "-";
					string p2 = sPreTxt.Length > 1 ? sPreTxt.Substring(sPreTxt.Length - 2, 1) : "-";
					if(u.Contains(p1))
					{
						int P1index = UNI.IndexOf(p1.ToString());
						if("hHqQ".Contains(p2))
						{
							bool Up = "hH".Contains(p2) && "uU".Contains(p1) && "oO".IndexOf(sPreChar) < 0 && newChar == sPreChar;
							sText = (sPreTxt.Length > 1 ? sPreTxt.Substring(0, sPreTxt.Length - 1) : "") + UNI[UNI.IndexOf(p1) + (Up ? 6 : 0) - SubtracIndex(p1) - GetSignIndex(P1index)] + UNI[index + (newChar == sPreChar && !Up ? 0: 12) - SubtracIndex(sPreChar)+ GetSignIndex(P1index)] + sNextTxt + (newChar == sPreChar && !Up ? cCurChar.ToString() : "");
						}
						else sText = (sPreTxt.Length > 1 ? sPreTxt.Substring(0, sPreTxt.Length - 1) : "") + UNI[UNI.IndexOf(p1) + (newChar == sPreChar ? 0 : 6) - SubtracIndex(p1) - GetSignIndex(P1index)] + UNI[index + (newChar == sPreChar ? 0: 12) - SubtracIndex(sPreChar) + GetSignIndex(P1index)] + sNextTxt + (newChar == sPreChar ? cCurChar.ToString() : "");
					}
				}
            	return sText;
            }
            
            if(cCurChar == TypingMethod[TMD.DD] && "dD".Contains(sPreChar))
	            	return sPreTxt + UNI[index + 1] + sNextTxt;
	            
            if(cCurChar == TypingMethod[TMD.DD] && "đĐ".Contains(sPreChar))
	            	return sPreTxt + UNI[index - 1] + sNextTxt + cCurChar;
	            
            if ("oO".Contains(cCurChar.ToString()) && uw.Contains(sPreChar)) //ưo => ươ
			{
				int Cindex = UNI.IndexOf(cCurChar.ToString());
				return sPreTxt + UNI[index - GetSignIndex(index)] + UNI[Cindex + 12 + GetSignIndex(index)] + sNextTxt;
			}
			
		    return sText;
		}
		
		private int SubtracIndex(string sChar)
		{
			int index = UNI.IndexOf(sChar.ToLower());
			string cstr = UNI[index - GetSignIndex(index)];
			if("ă,ê,ô,ư".Contains(cstr)) return 6;
			if("â,ơ".Contains(cstr)) return 12;
			//if("a,e,o,u".Contains(cstr)) 
			return 0;
		}
		
		/// <summary>
		/// Kiểm tra có bỏ dấu được không
		/// </summary>
		/// <param name="sPreTxt">Chuỗi ở trước</param>
		/// <param name="sPreChar">Ký tự cần kiểm tra</param>
		/// <param name="cCurChar">Dấu sẽ bỏ</param>
		/// <returns></returns>
		bool Check(string sPreTxt, string sPreChar, char cCurChar, int Cindex, int Pindex)
		{
			if(UNI.IndexOf(sPreChar) < 0) return false;
			// kiểm tra phụ âm đầu
			if(CheckSpellingLevel > 0 && Pindex > 0)
			{
				int len = Pindex - 1;
				while(len > -1 && sPreTxt.Length > 0 && UNI.Contains(sPreTxt.Substring(len, 1)) && "dđDĐ".IndexOf(sPreTxt.Substring(len, 1)) < 0) len--; //tìm độ dài phụ âm đầu
				if(((sPreTxt.Length > 1 && "qu,gi".IndexOf(sPreTxt.Substring(0,2).ToLower()) < 0) || sPreTxt.Length < 1)
				   && (len > -1 && PhuAm.IndexOf(sPreTxt.Substring(0, len + 1).ToLower()) < 0)) 
					return false;
			}
			string p1 = sPreTxt.Length > 0 ? sPreTxt.Substring(sPreTxt.Length - 1, 1) : "";
			string p2 = sPreTxt.Length > 1 ? sPreTxt.Substring(sPreTxt.Length - 2, 1) : "";
			string Vowels3 = Encodings.ConvertToUnsign(p2 + p1 + sPreChar).ToLower();
			if(p2.Length > 0)
			{
				if("uou,ieu,uyu,yeu,oai,uoi,uay,oay,uya,oeo".Contains(Vowels3)) return false;
			}
			if(p1.Length > 0)
			{
				string Vowels2 = Encodings.ConvertToUnsign(p1 + sPreChar).ToLower();
				if((p1 + sPreChar).ToLower() != "uâ")
					if(!(p2.Length > 0 && "qua,que,gia,giu,quy,qui".Contains(Vowels3)) && 
					   ("ai,oi,ui,ao,eo,au,eu,uu,iu,ay,uy,ia".Contains(Vowels2)
					    || (Vowels2 == "ua" && (cCurChar != TypingMethod[TMD.DauMuAOE] && cCurChar != TypingMethod[TMD.AA])))) 
						return false;
				if("oe" == Vowels2 && "oO".Contains(cCurChar.ToString())) 
					return false;
			}
            if(cCurChar == TypingMethod[TMD.Sac]
        		|| cCurChar == TypingMethod[TMD.Huyen]
        		|| cCurChar == TypingMethod[TMD.Nga]
        		|| cCurChar == TypingMethod[TMD.Hoi]
        		|| cCurChar == TypingMethod[TMD.Nang]
        		|| cCurChar == TypingMethod[TMD.XoaDau]
				||((cCurChar == TypingMethod[TMD.AW] ||cCurChar == TypingMethod[TMD.AA]) && a.Contains(sPreChar))
	            ||(cCurChar == TypingMethod[TMD.DauMuAOE] && aoe.Contains(sPreChar))
	            ||(cCurChar == TypingMethod[TMD.DD] && "dđDĐ".Contains(sPreChar))
	        	||(cCurChar == TypingMethod[TMD.EE] && e.Contains(sPreChar))
	        	||(cCurChar == TypingMethod[TMD.OThanh6] && o.Contains(sPreChar))
				||((cCurChar == TypingMethod[TMD.UOAW] || cCurChar == TypingMethod[TMD.DauMocUOA]) && uoa.Contains(sPreChar))
		        ||(cCurChar == TypingMethod[TMD.DauMocUO] && uo.Contains(sPreChar))
	            ||("oO".Contains(cCurChar.ToString()) && uw.Contains(sPreChar) && Cindex == Pindex + 1))
         		return true;
         	else 
         		return false;
		}
		/// <summary>
		/// get sign index
		/// </summary>
		/// <param name="index">index in Encoding</param>
		/// <returns></returns>
		private int GetSignIndex(int index) { return index < 73 ? index % 6 : (index - 74) % 6; }
		
		public bool isVNWord(string sText)
		{
			string sUnsign = Encodings.ConvertToUnsign(sText);
			if(sUnsign == sText) return true;
			string sWord = sText.ToLower();
			string pad3 = sWord.Length > 2 ? sWord.Substring(0, 3) : "";
			string pad2 = sWord.Length > 1 ? sWord.Substring(0, 2) : "";
			string pad1 = sWord.Substring(0, 1);
			string PAD = "";
			if(pad3.Length > 0 && PhuAm.Contains(pad3)) PAD = pad3;
			else if(pad2.Length > 0 && PhuAm.Contains(pad2)) PAD = pad2;
			else if(PhuAm.Contains(pad1)) PAD = pad1;
			
			int s1 = 0; int s2 = 0; int s3 = 0;
			string na3 = sWord.Length > PAD.Length + 2 ? RemoveSign(sWord.Substring(PAD.Length, 3), out s3) : "";
			string na2 = sWord.Length > PAD.Length + 1 ? RemoveSign(sWord.Substring(PAD.Length, 2), out s2) : "";
			string na1 = sWord.Length > PAD.Length ? RemoveSign(sWord.Substring(PAD.Length, 1), out s1) : "";
			string NA = "";
			int sign = 0;
			if(na3.Length > 0 && NguyenAm.Contains(na3)) {NA = na3; sign = s3;}
			else if(na2.Length > 0 && NguyenAm.Contains(na2)) {NA = na2; sign = s2;}
			else if(NguyenAm.Contains(na1)) {NA = na1; sign = s1;}
			
			string PAC = sWord.Length > PAD.Length + NA.Length ? sWord.Substring(PAD.Length + NA.Length) : "";
			if(NA.Length > 0 && PAD.Length > 0 && CheckSpellingLevel > 1)
			{
				string cNA = NA.Substring(0, 1);
				if(((new List<string>(){"g","ng"}).Contains(PAD) && "ieê".Contains(cNA) && sUnsign.ToLower() != "gi")
					||((new List<string>(){"gh","ngh"}).Contains(PAD) && "ieê".IndexOf(cNA) < 0)
					||(PAD == "c" && ((new List<string>(){"oa","oă","oe","uy","uơ","uô","uê","uâ","oai","oay","uây","uyê","uya","oeo","oao"}).Contains(NA) || "yieê".Contains(cNA)))
					||(PAD == "k" && "yieê".IndexOf(cNA) < 0)
					||(PAD == "qu" && "ou".Contains(cNA) && NA.Length > 1)
				)
					return false;
			}
			if(PAC.Length > 0)
			{
				if(!PhuAmCuoi.Contains(PAC) && CheckSpellingLevel > 0) return false;
				if(CheckSpellingLevel > 1 && SacNang.Contains(PAC) && sign != TMD.Sac && sign != TMD.Nang) return false;
			}
			else if(CheckSpellingLevel > 1 && NA.Length > 0 && NguyenAmGiua.Contains(NA)) return false;
			if(PAD.Length > 0 && PAC.Length > 0 && NA.Length == 0 || PAD.Length + PAC.Length + NA.Length < sText.Length) return false;
			return true;
		}
		private string RemoveSign(string stext, out int sign)
		{
			for(int i = 0; i < stext.Length; i++)
			{
				int index = UNI.IndexOf(stext[i].ToString());
				sign = GetSignIndex(index);
				if(sign > 0)
				{
					char[] arr = stext.ToCharArray();
					arr[i] = UNI[index - sign][0];
					return new String(arr);
				}
			}
			sign = 0;
			return stext;
		}
		
		/// <summary>
		/// Kiem tra phu am cuoi
		/// </summary>
		/// <returns></returns>
		public bool CheckLastConsonant(string sText, char sCurChar)
		{
			string str1 = sText.Substring(sText.Length - 1).ToLower();
			string str2 = sText.Length > 1 ? sText.Substring(sText.Length - 2).ToLower() : "";
			if(str2.Length > 1) // str2 != ""
				if(PhuAmCuoi.Contains(str2))
					if(SacNang.Contains(str2))
						return sCurChar != TypingMethod[TMD.Hoi] && sCurChar != TypingMethod[TMD.Nga] && sCurChar != TypingMethod[TMD.Huyen];
					else
						return true;
				else 
					if(PhuAm.Contains(str2) && sText.ToLower() != "gi") return false;
			if(PhuAmCuoi.Contains(str1))
				if(SacNang.Contains(str1))
					return sCurChar != TypingMethod[TMD.Hoi] && sCurChar != TypingMethod[TMD.Nga] && sCurChar != TypingMethod[TMD.Huyen];
				else
					return true;
			else return !PhuAm.Contains(str1);
		}
	}
}
